package com.jichaoyun.sys.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jichaoyun.common.utils.PoiExcelStyle;
import com.jichaoyun.model.entity.StationInfo;

import com.jichaoyun.common.model.PagedResult;
import com.jichaoyun.model.entity.FlowDay;
import com.jichaoyun.model.entity.FlowHour;

import com.jichaoyun.sys.mapper.FlowDayMapper;
import com.jichaoyun.sys.mapper.FlowHourMapper;
import com.jichaoyun.sys.mapper.StationInfoMapper;
import com.jichaoyun.sys.service.IFlowDayService;
import com.jichaoyun.sys.service.IStationInfoService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
public class FlowDayServiceImpl extends ServiceImpl<FlowDayMapper, FlowDay>
            implements IFlowDayService {

    @Resource
    private IStationInfoService stationInfoService;

    @Resource
    private StationInfoMapper stationInfoMapper;

    @Resource
    private FlowDayMapper flowDayMapper;

    @Resource
    private FlowHourMapper flowHourMapper;

    /**
     * 获取某月站点月报信息
     * @param station_no
     * @param year
     * @param month
     * @return
     */
    @Override
    public Map<String, Object> getStationMonthForm(Integer station_no, Integer year, Integer month) {
        Map<String, Object> map = new LinkedHashMap<>();

        //获取每天结算的小时
        Integer accountHour = stationInfoService.getAccountHourByStation(station_no);

        // 获取上个月的最后一次结算时间
        LocalDateTime startOfMonthTime;
        if(month==1){
             startOfMonthTime = LocalDateTime.of(year-1,12,31,accountHour,0,0);
        }else {
            LocalDate d1 = LocalDate.of(year,month-1,1);
            startOfMonthTime = LocalDateTime.of(year,month-1,d1.lengthOfMonth(),accountHour,0,0);
        }

        // 获取本月的最后一次结算时间
        LocalDate d2 = LocalDate.of(year,month,1);
        LocalDateTime endOfMonthTime = LocalDateTime.of(year,month,d2.lengthOfMonth()-1,accountHour,2,0);

        LambdaQueryWrapper<FlowDay> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FlowDay::getStationNo,station_no)
                .ge(FlowDay::getRecStart,startOfMonthTime)
                .le(FlowDay::getRecStart,endOfMonthTime)
                .orderByAsc(FlowDay::getId);

        List<FlowDay> flowDayList = flowDayMapper.selectList(wrapper);


        for (FlowDay flowDay : flowDayList) {
            if(flowDay.getFTotalEnd()!=null){
                LocalDateTime day = flowDay.getRecStart().plusHours(24);
                map.put(day.getDayOfMonth() + "日",flowDay.getFTotalEnd()-flowDay.getFTotalStart());
            }
        }
        return map;
    }

    /**
     * 单用户月报表，导出到Excel
     * @param station_no
     * @param year
     * @param month
     * @param response
     * @throws IOException
     */
    @Override
    public void getUserMonthDataExcel(Integer station_no , Integer year, Integer month, HttpServletResponse response) throws IOException {


        Map<String,Object> map1 = getStationMonthForm(station_no,year,month);

        List<Map<String,Object>> resList = new ArrayList<>();
        resList.add(map1);

        // 创建一个 ExcelWriter 把 list 数据用这个writer写出来(生成出来)
        ExcelWriter wr = ExcelUtil.getWriter( true);
        wr.write(resList, true);

        //设置excel的导出样式
        PoiExcelStyle.setStyleSet(wr,resList);

        LocalDate theDate = LocalDate.of(year,month,1);
        // 创建 DateTimeFormatter 对象，定义日期格式
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy年M月 ");
        // 使用 DateTimeFormatter 格式化 LocalDateTime 对象
        String formattedDate = theDate.format(formatter1);

        // 把这个 excel 下载下来
        response.setContentType("application/vnd.gpenxmlfonmats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode(formattedDate+"单用户月报表", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader( "Content-Disposition", "attachment;filename="+fileName+".xlsx");
        ServletOutputStream out = response.getOutputStream();
        wr.flush(out,true);
        wr.close();
        IoUtil.close(System.out);

    }

    /**
     * //获取仪表某天结算时候的总数
     * zz
     * @param station_no
     * @param theDay
     * @return
     */
    public Float getSettleTotalByDate(Integer station_no,LocalDateTime theDay){
        //获取此站点每天结算的小时
        Integer accountHour = stationInfoService.getAccountHourByStation(station_no);

        // 获取日报表的结算时间,前后各差5分钟，容纳结算的时候有延迟5分钟的风险误差
        LocalDateTime startOfDayTime = theDay.minusHours(24).plusHours(accountHour).minusMinutes(5);
        LocalDateTime endOfDayTime = theDay.minusHours(24).plusHours(accountHour).plusMinutes(5);

        LambdaQueryWrapper<FlowDay> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FlowDay::getStationNo,station_no)
                .ge(FlowDay::getRecStart,startOfDayTime)
                .le(FlowDay::getRecStart,endOfDayTime);
        FlowDay flowDay = flowDayMapper.selectOne(wrapper);
        if(flowDay==null){
            return -1f;
        }else {
            return flowDay.getFTotalEnd();
        }
    }

    /**
     * //获取仪表某天的流量
     * zz
     * @param station_no
     * @param theDay
     * @return
     */
    public Float getSettleFlowByDate(Integer station_no,LocalDateTime theDay){
        //获取此站点每天结算的小时
        Integer accountHour = stationInfoService.getAccountHourByStation(station_no);

        // 获取日报表的结算时间,前后各差1分钟，容纳结算的时候有延迟1分钟的风险误差
        LocalDateTime startOfDayTime = theDay.minusHours(24).plusHours(accountHour).minusMinutes(5);
        LocalDateTime endOfDayTime = theDay.minusHours(24).plusHours(accountHour).plusMinutes(5);

        LambdaQueryWrapper<FlowDay> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FlowDay::getStationNo,station_no)
                .ge(FlowDay::getRecStart,startOfDayTime)
                .le(FlowDay::getRecStart,endOfDayTime);
        FlowDay flowDay = flowDayMapper.selectOne(wrapper);
        if(flowDay==null){
            return -1f;
        }
        return flowDay.getFTotalEnd() - flowDay.getFTotalStart();
    }


    /**
     * 获取分支节点的站表的日管损
     * @param station_no
     * @param theDay
     * @return
     */
    public Float getLossByDateAndStation(Integer station_no, LocalDateTime theDay){
        //获取此站点当天0到24小时的流量
        Float station_flow =  getDayFlow0_24(station_no,theDay);

        //获取该分支表下的孩子节点有哪些
        LambdaQueryWrapper<StationInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StationInfo::getFatherStationNo,station_no);
        List<StationInfo> childrenList = stationInfoMapper.selectList(wrapper);

        //计算子节点的流量之和
        Float childrens_flow = 0F;
        for (StationInfo childInfo : childrenList) {
            childrens_flow += getDayFlow0_24(childInfo.getStationNo(),theDay);
        }

        return station_flow - childrens_flow;
    }

    /**
     * 获取分支节点的站表的日管损率
     * @param station_no
     * @param theDay
     * @return
     */
    public Float getLossRateByDateAndStation(Integer station_no, LocalDateTime theDay){
        //获取此站点当天0到24小时的流量
        Float station_flow =  getDayFlow0_24(station_no,theDay);
        if(station_flow==0 ||station_flow==null){
            return -1f;
        }

        //获取该分支表下的孩子节点有哪些
        LambdaQueryWrapper<StationInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StationInfo::getFatherStationNo,station_no);
        List<StationInfo> childrenList = stationInfoMapper.selectList(wrapper);

        //计算子节点的流量之和
        Float childrens_flow = 0F;
        for (StationInfo childInfo : childrenList) {
            childrens_flow += getDayFlow0_24(childInfo.getStationNo(),theDay);
        }

        //四舍五入保留四位小数
        Float f = (station_flow - childrens_flow)/station_flow;
        return Float.valueOf(String.format("%.4f", f));

    }



    /**
     * 获取某天某站0-24小时的流量使用量
     * @param station_no
     * @param theDay
     * @return
     */
    private Float getDayFlow0_24(Integer station_no, LocalDateTime theDay) {
        //获取此站点当天的流量
        LocalDateTime startOfDayTime = theDay.toLocalDate().atStartOfDay().minusMinutes(2);
        LocalDateTime endOfDayTime = theDay.toLocalDate().atStartOfDay().plusHours(24).plusHours(2);
        //获取当天开始的流量
        LambdaQueryWrapper<FlowHour> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FlowHour::getStation, station_no)
                .le(FlowHour::getRecEnd,endOfDayTime)
                .ge(FlowHour::getRecStart,startOfDayTime)
                .orderByAsc(FlowHour::getRecStart);
        List<FlowHour> flowHourList = flowHourMapper.selectList(wrapper);

        if (flowHourList==null || flowHourList.size()==0){
            return -1f;
        }
        //获取该支表当天的流量
         return flowHourList.get(flowHourList.size()-1).getFStart()- flowHourList.get(0).getFStart();
    }

    /**
     * 获取某天某站截至到该月当日流量使用量
     * @param station_no
     * @param theDay
     * @return
     */
    private Float getMonthFlowUpToDate(Integer station_no, LocalDateTime theDay) {
        //获取此站点当天的流量
        LocalDateTime startOfDayTime = theDay.toLocalDate().withDayOfMonth(1).atStartOfDay().minusMinutes(2);
        LocalDateTime endOfDayTime = theDay.plusDays(1);
        //获取当天开始的流量
        LambdaQueryWrapper<FlowHour> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FlowHour::getStation, station_no)
                .le(FlowHour::getRecEnd,endOfDayTime)
                .ge(FlowHour::getRecStart,startOfDayTime)
                .orderByAsc(FlowHour::getRecStart);
        List<FlowHour> flowHourList = flowHourMapper.selectList(wrapper);

        if (flowHourList==null || flowHourList.size()==0){
            return -1f;
        }
        //获取该支表当月截至当天的流量
        return flowHourList.get(flowHourList.size()-1).getFStart()- flowHourList.get(0).getFStart();
    }

    /**
     * 根据日期查询全部用户
     * @param theDay
     * @param pageNo
     * @return
     */
    @Override
    public PagedResult getAllMultiStationDayFormPage(LocalDateTime theDay, Integer pageNo) {
        //保存返回结果
        PagedResult pagedResult = new PagedResult();

        List<Map<String, Object>> resList = new ArrayList<>();
        //分页查找所有未被删除的站点信息表
        IPage<StationInfo> infoIPage = stationInfoService.getAllStationInfo(pageNo,10);
        List<StationInfo> stationInfos = infoIPage.getRecords();
        pagedResult.setTotal((int) infoIPage.getTotal());
        pagedResult.setPages((int) infoIPage.getPages());

        //查找每个用户的信息
        for (StationInfo stationInfo : stationInfos) {
            HashMap<String , Object> resMap = new HashMap<>();
            Integer station_no = stationInfo.getStationNo();
            resMap.put("用户站号",station_no);
            resMap.put("用户站名",stationInfo.getStationName());
            //获取仪表某天结算时候的总数
            Float theDayTotal = getSettleTotalByDate(station_no,theDay);
            Float LastDayTotal = getSettleTotalByDate(station_no,theDay.minusHours(24));
            Boolean total_is_increased = (theDayTotal - LastDayTotal) > 0;
            resMap.put("仪表总数",theDayTotal);
            resMap.put("仪表总量同比前日",Math.abs(theDayTotal - LastDayTotal));
            resMap.put("总量对比是否增长",total_is_increased);

            //获取仪表某天结算时候的当日蒸汽使用量
            Float theDayFlow = getSettleFlowByDate(station_no,theDay);
            Float LastDayFlow = getSettleFlowByDate(station_no,theDay.minusHours(24));
            Boolean flow_is_increased = theDayFlow - LastDayFlow > 0;
            resMap.put("当日流量",theDayFlow);
            resMap.put("流量同比前日",Math.abs(theDayFlow - LastDayFlow));
            resMap.put("流量对比是否增长",flow_is_increased);

            if(stationInfo.getIsLeaf()==1){
                resMap.put("管损",null);
                resMap.put("管损率",null);
            }else {
                Float theLoss = getLossByDateAndStation(station_no,theDay);
                Float lastLoss = getLossByDateAndStation(station_no,theDay.minusHours(24));
                resMap.put("管损",theLoss);
                resMap.put("管损同前日比较",Math.abs(theLoss-lastLoss));
                resMap.put("管损是否增长",(theLoss-lastLoss)>0);

                Float theLossRate = getLossRateByDateAndStation(station_no,theDay);
                Float lastLossRate = getLossRateByDateAndStation(station_no,theDay.minusHours(24));
                resMap.put("管损率",theLossRate);
                resMap.put("管损率同前日比较",Math.abs(theLossRate-lastLossRate));
                resMap.put("管损率是否增长",(theLossRate- lastLossRate)>0);
            }

            //查询当月截至到某天的用汽量
            Float monthFlowUpToDate = getMonthFlowUpToDate(station_no, theDay);
            Float lastMonthFlowUpToDate = getMonthFlowUpToDate(station_no, theDay.minusMonths(1));
            resMap.put("该月当日用汽累计",monthFlowUpToDate);
            resMap.put("当日相比前月",Math.abs(monthFlowUpToDate-lastMonthFlowUpToDate));
            resMap.put("当日相比前月累计是否增长",(monthFlowUpToDate-lastMonthFlowUpToDate)>0 );

            resList.add(resMap);
        }
        pagedResult.setRows(resList);

        return pagedResult;
    }


    /**
     * 导出全部的用户到Excel
     * @param theDay
     * @param response
     * @throws IOException
     */
    @Override
    public void getAllMultiStationDayForm(LocalDateTime theDay, HttpServletResponse response) throws IOException {


        List<Map<String, Object>> resList = new ArrayList<>();
        //查找所有未被删除的站点信息表
        List<StationInfo> stationInfos = stationInfoService.getAllStationInfo();

        //查找每个用户的信息
        for (StationInfo stationInfo : stationInfos) {
            HashMap<String , Object> resMap = new LinkedHashMap<>();
            Integer station_no = stationInfo.getStationNo();
            resMap.put("用户站号",station_no);
            resMap.put("用户站名",stationInfo.getStationName().trim());
            //获取仪表某天结算时候的总数
            Float theDayTotal = getSettleTotalByDate(station_no,theDay);
            Float LastDayTotal = getSettleTotalByDate(station_no,theDay.minusHours(24));
            resMap.put("仪表总数",theDayTotal);
            resMap.put("仪表总量同比前日",theDayTotal - LastDayTotal);

            //获取仪表某天结算时候的当日蒸汽使用量
            Float theDayFlow = getSettleFlowByDate(station_no,theDay);
            Float LastDayFlow = getSettleFlowByDate(station_no,theDay.minusHours(24));
            resMap.put("当日流量",theDayFlow);
            resMap.put("流量同比前日",theDayFlow - LastDayFlow);


            if(stationInfo.getIsLeaf()==1){
                resMap.put("管损",null);
                resMap.put("管损率",null);
            }else {
                Float theLoss = getLossByDateAndStation(station_no,theDay);
                Float lastLoss = getLossByDateAndStation(station_no,theDay.minusHours(24));
                resMap.put("管损",theLoss);
                resMap.put("管损同前日比较",theLoss-lastLoss);

                Float theLossRate = getLossRateByDateAndStation(station_no,theDay);
                Float lastLossRate = getLossRateByDateAndStation(station_no,theDay.minusHours(24));
                resMap.put("管损率",theLossRate);
                resMap.put("管损率同前日比较",theLossRate-lastLossRate);
            }

            //查询当月截至到某天的用汽量
            Float monthFlowUpToDate = getMonthFlowUpToDate(station_no, theDay);
            Float lastMonthFlowUpToDate = getMonthFlowUpToDate(station_no, theDay.minusMonths(1));
            resMap.put("该月当日用汽累计",monthFlowUpToDate);
            resMap.put("当日相比前月",monthFlowUpToDate-lastMonthFlowUpToDate);

            resList.add(resMap);
        }
        // 创建一个 ExcelWriter 把 list 数据用这个writer写出来(生成出来)
        ExcelWriter wr = ExcelUtil.getWriter( true);
        wr.write(resList, true);

        //设置excel的导出样式
        PoiExcelStyle.setStyleSet(wr,resList);

        // 创建 DateTimeFormatter 对象，定义日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年M月d日 ");
        // 使用 DateTimeFormatter 格式化 LocalDateTime 对象
        String formattedDate = theDay.format(formatter);

        // 把这个 excel 下载下来
        response.setContentType("application/vnd.gpenxmlfonmats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode(formattedDate+"多用户日报表", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader( "Content-Disposition", "attachment;filename="+fileName+".xlsx");
        ServletOutputStream out = response.getOutputStream();
        wr.flush(out,true);
        wr.close();
        IoUtil.close(System.out);

    }

    /**
     * 根据站号、日期查询多用户中日报表
     * @param theDay
     * @param stationNo
     * @return
     */
    @Override
    public Map<String, Object> getMultiStationDayFormByStation(LocalDateTime theDay, Integer stationNo) {
        HashMap<String , Object> resMap = new LinkedHashMap<>();
        StationInfo stationInfo = stationInfoMapper.selectById(stationNo);

        Integer station_no = stationInfo.getStationNo();
        resMap.put("用户站号",station_no);
        resMap.put("用户站名",stationInfo.getStationName());
        //获取仪表某天结算时候的总数
        Float theDayTotal = getSettleTotalByDate(station_no,theDay);
        Float LastDayTotal = getSettleTotalByDate(station_no,theDay.minusHours(24));
        Boolean total_is_increased = (theDayTotal - LastDayTotal) > 0;
        resMap.put("仪表总数",theDayTotal);
        resMap.put("仪表总量同比前日",Math.abs(theDayTotal - LastDayTotal));
        resMap.put("总量对比是否增长",total_is_increased);

        //获取仪表某天结算时候的当日蒸汽使用量
        Float theDayFlow = getSettleFlowByDate(station_no,theDay);
        Float LastDayFlow = getSettleFlowByDate(station_no,theDay.minusHours(24));
        Boolean flow_is_increased = theDayFlow - LastDayFlow > 0;
        resMap.put("当日流量",theDayFlow);
        resMap.put("流量同比前日",Math.abs(theDayFlow - LastDayFlow));
        resMap.put("流量对比是否增长",flow_is_increased);

        if(stationInfo.getIsLeaf()==1){
            resMap.put("管损",null);
            resMap.put("管损率",null);
        }else {
            Float theLoss = getLossByDateAndStation(station_no,theDay);
            Float lastLoss = getLossByDateAndStation(station_no,theDay.minusHours(24));
            resMap.put("管损",theLoss);
            resMap.put("管损同前日比较",Math.abs(theLoss-lastLoss));
            resMap.put("管损是否增长",(theLoss-lastLoss)>0);

            Float theLossRate = getLossRateByDateAndStation(station_no,theDay);
            Float lastLossRate = getLossRateByDateAndStation(station_no,theDay.minusHours(24));
            resMap.put("管损率",theLossRate);
            resMap.put("管损率同前日比较",Math.abs(theLossRate-lastLossRate));
            resMap.put("管损率是否增长",(theLossRate- lastLossRate)>0);
        }

        //查询当月截至到某天的用汽量
        Float monthFlowUpToDate = getMonthFlowUpToDate(station_no, theDay);
        Float lastMonthFlowUpToDate = getMonthFlowUpToDate(station_no, theDay.minusMonths(1));
        resMap.put("该月当日用汽累计",monthFlowUpToDate);
        resMap.put("当日相比前月",Math.abs(monthFlowUpToDate-lastMonthFlowUpToDate));
        resMap.put("当日相比前月累计是否增长",(monthFlowUpToDate-lastMonthFlowUpToDate)>0 );

        return resMap;
    }


}
